package com.hanxiaozhang.watermark.example2;


import com.hanxiaozhang.watermark.example1.WatermarkPositionEnum;
import com.hanxiaozhang.watermark.example1.WatermarkTypeEnum;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Comparator;
import java.util.List;


/**
 * 〈一句话功能简述〉<br>
 * 〈添加水印工具类〉
 * <p>
 * Graphics2D类的x轴，y轴：
 * .     (0,0)             x轴
 * .      -------------------→
 * .      |
 * .      |
 * .      |
 * .      |
 * .      |
 * . y轴  ↓
 *
 * @author hanxinghua
 * @create 2024/7/14
 * @since 1.0.0
 */
@Slf4j
public class WatermarkUtil {


    /**
     * 一个空格
     */
    public static final String ONE_STRING = " ";

    /**
     * 添加水印
     *
     * @param bufferedImage
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     * @return
     */
    public static BufferedImage addWatermark(BufferedImage bufferedImage, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        // 画笔
        Graphics2D graphics = null;
        try {

            // 校验配置信息
            checkConfig(watermarks, watermarkBufferedImage, config);
            // 创建画笔
            graphics = bufferedImage.createGraphics();
            // 设置画笔
            setGraphics(graphics, config);
            // 设置水印
            setWatermark(graphics, watermarks, watermarkBufferedImage, config);
            // 图片宽度
            Integer imageWidth = bufferedImage.getWidth();
            // 图片高度
            Integer imageHeight = bufferedImage.getHeight();

            switch (config.getWatermarkPosition()) {
                // 居中
                case CENTER:
                    centerDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 左侧
                case LEFT_SIDE:
                    leftSideDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 右侧
                case RIGHT_SIDE:
                    rightSideDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 正上方
                case DIRECTLY_ABOVE:
                    directlyAboveDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 正下方
                case DIRECTLY_BELOW:
                    directlyBelowDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 左上角
                case UPPER_LEFT:
                    upperLeftDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 左下角
                case LOWER_LEFT:
                    lowerLeftDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 右上角
                case UPPER_RIGHT:
                    upperRightDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 右下角
                case LOWER_RIGHT:
                    lowerRightDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 平铺
                case TILE:
                    tileDrawDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                // 倾斜平铺
                case TILT_TILE:
                    graphics.rotate(config.getWatermarkTiltTileAngle());
                    tileDrawDraw(graphics, imageWidth, imageHeight, watermarks, watermarkBufferedImage, config);
                    break;
                default: // 错误的操作类型
                    throw new RuntimeException("错误的操作类型");
            }


        } catch (Exception e) {
            log.error("添加水印异常，异常信息：{}", e);
        } finally {
            if (graphics != null) {
                graphics.dispose();
            }
        }
        return bufferedImage;
    }


    /**
     * 校验配置信息
     * <p>
     *
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    private static void checkConfig(List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {


        if (config.getWatermarkType() == null) {
            throw new RuntimeException("水印类型为空");
        }

        if (config.getWatermarkPosition() == null) {
            throw new RuntimeException("水印位置为空");
        }

        if (WatermarkTypeEnum.TEXT_WATERMARK.equals(config.getWatermarkType())) {

            if (watermarks == null || watermarks.isEmpty()) {
                throw new RuntimeException("文本水印为空");
            }

            if (config.getWatermarkFont() == null) {
                throw new RuntimeException("水印字体为空");
            }

            if (config.getWatermarkColor() == null) {
                throw new RuntimeException("水印颜色为空");
            }

            if (watermarks.size() > 1) {
                if (config.getWatermarkTextPosition() == null) {
                    throw new RuntimeException("多行水印文本位置为空");
                }

            }

            if (WatermarkPositionEnum.TILE.equals(config.getWatermarkPosition())
                    || WatermarkPositionEnum.TILT_TILE.equals(config.getWatermarkPosition())) {

                if (config.getWatermarkTileSplitMultiple() == null) {
                    throw new RuntimeException("水印平铺间隔倍数为空");
                }
            }

            if (WatermarkPositionEnum.TILT_TILE.equals(config.getWatermarkPosition())) {
                if (config.getWatermarkTiltTileAngle() == null) {
                    throw new RuntimeException("水印倾斜平铺角度为空");
                }
            }
        } else {

            if (watermarkBufferedImage == null) {
                throw new RuntimeException("图片水印为空");
            }
        }


    }


    /**
     * 设置画笔
     *
     * @param graphics
     * @param config
     */
    private static void setGraphics(Graphics2D graphics, WatermarkConfig config) {

        if (WatermarkTypeEnum.TEXT_WATERMARK.equals(config.getWatermarkType())) {
            // 设置字体
            graphics.setFont(config.getWatermarkFont());
            // 设置颜色
            graphics.setColor(config.getWatermarkColor());
        }
    }


    /**
     * 设置水印相关
     *
     * @param graphics
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    private static void setWatermark(Graphics2D graphics, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        // 文字水印
        if (WatermarkTypeEnum.TEXT_WATERMARK.equals(config.getWatermarkType())) {

            // 获取最长的文本
            String longWatermark = watermarks.stream()
                    .sorted(Comparator.comparing(String::length).reversed()).findFirst().orElse(null);

            // 计算水印宽度和高度
            config.setWatermarkWidth(getWatermarkLength(longWatermark, graphics));
            Integer watermarkHeight = graphics.getFontMetrics().getHeight();
            if (watermarks.size() > 1) {
                // 行间距
                int leading = graphics.getFontMetrics().getLeading();
                watermarkHeight = config.getWatermarkFont().getSize() * watermarks.size() + leading * (watermarks.size() - 1);
            }
            config.setWatermarkHeight(watermarkHeight);
        } else {
            // 计算水印宽度和高度
            config.setWatermarkWidth(watermarkBufferedImage.getWidth());
            config.setWatermarkHeight(watermarkBufferedImage.getHeight());
        }
    }


    /**
     * 居中
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    private static void centerDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = (imageWidth - config.getWatermarkWidth()) / 2;
        Integer y = (imageHeight - config.getWatermarkHeight()) / 2;
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }

    /**
     * 左侧
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    public static void leftSideDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = graphics.getFontMetrics().getAscent();
        Integer y = (imageHeight - config.getWatermarkHeight()) / 2;
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 右侧
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    public static void rightSideDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = imageWidth - config.getWatermarkWidth() - graphics.getFontMetrics().getAscent();
        Integer y = (imageHeight - config.getWatermarkHeight()) / 2;
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 正上方
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    private static void directlyAboveDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = (imageWidth - config.getWatermarkWidth()) / 2;
        Integer y = imageHeight - config.getWatermarkHeight() - graphics.getFontMetrics().getAscent();
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 正下方
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    private static void directlyBelowDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = (imageWidth - config.getWatermarkWidth()) / 2;
        Integer y = graphics.getFontMetrics().getAscent();
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 左上角
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    private static void upperLeftDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = graphics.getFontMetrics().getAscent();
        Integer y = graphics.getFontMetrics().getAscent();
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 左下角
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    private static void lowerLeftDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = graphics.getFontMetrics().getAscent();
        Integer y = imageHeight - config.getWatermarkHeight() - graphics.getFontMetrics().getAscent();
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 右上角
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    public static void upperRightDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = imageWidth - config.getWatermarkWidth() - graphics.getFontMetrics().getAscent();
        Integer y = graphics.getFontMetrics().getAscent();
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 右下角
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    public static void lowerRightDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer x = imageWidth - config.getWatermarkWidth() - graphics.getFontMetrics().getAscent();
        Integer y = imageHeight - config.getWatermarkHeight() - graphics.getFontMetrics().getAscent();
        drawWatermark(graphics, x, y, watermarks, watermarkBufferedImage, config);
    }


    /**
     * 平铺
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    public static void tileDrawDraw(Graphics2D graphics, Integer imageWidth, Integer imageHeight, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        Integer watermarkHeight = config.getWatermarkHeight();
        Integer watermarkWidth = config.getWatermarkWidth();
        // 间隔
        Integer split = watermarkHeight * config.getWatermarkTileSplitMultiple();

        // x,y可以绘制的数量
        int xCanNum = imageWidth / watermarkWidth + 1;
        int yCanNum = imageHeight / watermarkHeight + 1;
        for (int i = 0; i <= yCanNum; i++) {
            int y = watermarkHeight * i + split * i;
            for (int j = 0; j < xCanNum; j++) {
                int x = watermarkWidth * j + split * j;
                drawWatermark(graphics, x, y - (watermarkHeight + split) * j, watermarks, watermarkBufferedImage, config);
            }
        }
    }


    /**
     * 画水印
     *
     * @param graphics
     * @param x
     * @param y
     * @param watermarks
     * @param watermarkBufferedImage
     * @param config
     */
    public static void drawWatermark(Graphics2D graphics, int x, int y, List<String> watermarks, BufferedImage watermarkBufferedImage, WatermarkConfig config) {

        // 文字水印
        if (WatermarkTypeEnum.TEXT_WATERMARK.equals(config.getWatermarkType())) {

            for (String watermark : watermarks) {
                // 计算多文本居中时，每行文本的偏移量
                int offset = 0;
                if (watermarks.size() > 1 && WatermarkTextPositionEnum.CENTER.equals(config.getWatermarkTextPosition())) {
                    Integer longWatermarkWidth = config.getWatermarkWidth();
                    Integer watermarkLength = getWatermarkLength(watermark, graphics);
                    if (!longWatermarkWidth.equals(watermarkLength)) {
                        offset = (longWatermarkWidth - watermarkLength) / 2;
                    }
                }
                graphics.drawString(watermark, x + offset, y);
                // 下一行y轴位置
                y = config.getWatermarkHeight() + y;
            }
        } else {
            // 图片水印
            graphics.drawImage(watermarkBufferedImage, x, y - config.getWatermarkHeight(), config.getWatermarkWidth(),
                    config.getWatermarkHeight(), null);
        }
    }


    /**
     * 获取水印文字的长度
     *
     * @param watermarkContent 文字水印内容
     * @param graphics         图像类
     * @return
     */
    private static Integer getWatermarkLength(String watermarkContent, Graphics2D graphics) {

        return graphics.getFontMetrics(graphics.getFont()).charsWidth(watermarkContent.toCharArray(), 0,
                watermarkContent.length());
    }


    /**
     * 将图片写入到指定位置
     *
     * @param bufImg
     * @param tarImgPath
     */
    public static void writeImage(BufferedImage bufImg, String tarImgPath) {
        // 输出图片
        try {
            FileOutputStream outImgStream = new FileOutputStream(tarImgPath);
            ImageIO.write(bufImg, "png", outImgStream);
            outImgStream.flush();
            outImgStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 读取图片File
     *
     * @param imageFile
     * @return
     */
    public static BufferedImage readFile(File imageFile) {

        try {
            // 将文件对象转化为图片对象
            return ImageIO.read(imageFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 读取本地图片
     *
     * @param path
     * @return
     */
    public static BufferedImage readLocalPicture(String path) {

        if (null == path) {
            throw new RuntimeException("本地图片路径不能为空");
        }
        File imageFile = new File(path);
        return readFile(imageFile);
    }


    /**
     * 读取网络图片
     *
     * @param path 网络图片地址
     */
    public static BufferedImage readNetworkPicture(String path) {

        if (null == path) {
            throw new RuntimeException("网络图片路径不能为空");
        }

        try {
            // 创建一个URL对象,获取网络图片的地址信息
            URL url = new URL(path);
            // 将URL对象输入流转化为图片对象 (url.openStream()方法，获得一个输入流)
            BufferedImage bugImg = ImageIO.read(url.openStream());
            if (null == bugImg) {
                throw new RuntimeException("网络图片地址不正确");
            }
            return bugImg;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}
